/*
	Enigma route and waypoint file format.
        http://www.mglavionics.co.za/Docs/Enigma%20Waypoint%20format.pdf
        Binary data are stored in little endian (Intel)

	Copyright (C) 2009 Tobias Kretschmar, tobias.kretschmar@gmx.de

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA

 */

#include "defs.h"

#define MYNAME "Enigma binary route and waypoint file format"

#define WTYPE_WAYPOINT        0 // Waypoint of unspecified type
#define WTYPE_AIRPORT         1 // Typical assignment for medium sized airports
#define WTYPE_MAJORAIRPORT    2 // Typical assignment for large and international airports
#define WTYPE_SEAPLANEBASE    3
#define WTYPE_AIRFIELD        4 // Typical assignment for smaller municipal airfields, glider fields etc
#define WTYPE_PRIVATEAIRFIELD 5
#define WTYPE_ULTRALIGHTFIELD 6
#define WTYPE_INTERSECTION    7 // (reporting point, boundary crossing)
#define WTYPE_HELIPORT        8
#define WTYPE_TACAN           9
#define WTYPE_NDBDME          10
#define WTYPE_NDB             11
#define WTYPE_VORDME          12
#define WTYPE_VORTAC          13
#define WTYPE_FANMARKER       14
#define WTYPE_VOR             15
#define WTYPE_REPPT           16
#define WTYPE_LFR             17
#define WTYPE_UHFNDB          18
#define WTYPE_MNDB            19
#define WTYPE_MNDBDME         20
#define WTYPE_LOM             21
#define WTYPE_LMM             22
#define WTYPE_LOCSDF          23
#define WTYPE_MLSISMLS        24
#define WTYPE_OTHERNAV        25 // Navaid not falling into any of the above types
#define WTYPE_ALTITUDECHANGE  26 // Location at which altitude should be changed

union wpt_data {
    gbint32     wp_altitude;  // Waypoint type 0-6,8: waypoint altitude in feet
    gbint32     tg_altitude;  // Waypoint type 26: target altitude in feet
    gbuint32    frequency;    // Waypoint type 9-25: freq in steps of 1000Hz (118Mhz = 180000)
    gbint32     dummy;        // waypoint type 7, unused
};

typedef struct enigma_wpt {
	gbint32			latitude;
	gbint32			longitude;
    union wpt_data  data;
    gbuint8         waypoint_type;
    gbuint8         shortname_len;
    char            shortname[6];
    gbuint8         longname_len;
    char            longname[27];
} ENIGMA_WPT;

static gbfile *file_in, *file_out;

static void
rd_init(const char *fname)
{
	file_in = gbfopen_le(fname, "rb", MYNAME);
}

gbint32 decToEnigmaPosition (double val)
{
	int degrees = fabs(val);
	double frac = fabs(val) - degrees;
	int enigmadeg = degrees * 180000;
	int enigmafrac = 180000 * frac;
	int sign = (val < 0) ? -1 : +1;
	return sign * (enigmadeg + enigmafrac);
}

float enigmaPositionToDec (gbint32 val)
{
	int deg = abs(val) / 180000;
	int enigmafrac = abs(val) % 180000;
	double frac = (double)enigmafrac / 180000;
	int sign = (val < 0) ? -1 : +1;
	return sign * (deg + frac);
}

static void
data_read(void)
{
    struct enigma_wpt ewpt;
    route_head *route = route_head_alloc();
    route_add_head (route);

	while (1 == gbfread(&ewpt, sizeof (ewpt), 1, file_in)) {
		waypoint *wpt = waypt_new();
		wpt->latitude = enigmaPositionToDec(le_read32(&ewpt.latitude));
		wpt->longitude = enigmaPositionToDec(le_read32(&ewpt.longitude));
		wpt->shortname = xstrndup(ewpt.shortname, ewpt.shortname_len);
		wpt->description = xstrndup(ewpt.longname, ewpt.longname_len);
                switch (ewpt.waypoint_type)
                {
                    case WTYPE_WAYPOINT:        // 0
                    case WTYPE_AIRPORT:         // 1
                    case WTYPE_MAJORAIRPORT:    // 2
                    case WTYPE_SEAPLANEBASE:    // 3
                    case WTYPE_AIRFIELD:        // 4
                    case WTYPE_PRIVATEAIRFIELD: // 5
                    case WTYPE_ULTRALIGHTFIELD: // 6
                    case WTYPE_HELIPORT:        // 8
                        // waypoint altitude
                        wpt->altitude = FEET_TO_METERS(le_read32(&ewpt.data.wp_altitude) - 1000);
                        break;
                    case WTYPE_ALTITUDECHANGE:  // 26
                        // target altitude
                        wpt->altitude = FEET_TO_METERS(le_read32(&ewpt.data.tg_altitude) - 1000);
                        break;
                    case WTYPE_INTERSECTION:    // 7
                        // unused
                        break;
                    default:
                        // frequency
                        // wpt->frequency = wpt.le_readu32(ewpt.data.frequency);
                        ;
                }
		route_add_wpt(route, wpt);
	}
}

static void
rd_deinit(void)
{
	gbfclose(file_in);
}

static void
wr_init(const char *fname)
{
	file_out = gbfopen_le(fname, "wb", MYNAME);
}

static void
route_head_noop(const route_head *wp)
{
}

#ifndef min
#define min(a,b) ((a) < (b)) ? (a) : (b)
#endif
#ifndef max
#define max(a,b) ((a) > (b)) ? (a) : (b)
#endif

static void
enigma_waypt_disp(const waypoint *wpt)
{
	struct enigma_wpt ewpt;

	memset (&ewpt, 0, sizeof (ewpt));

	le_write32(&ewpt.latitude, decToEnigmaPosition(wpt->latitude));
	le_write32(&ewpt.longitude, decToEnigmaPosition(wpt->longitude));
    ewpt.waypoint_type = WTYPE_WAYPOINT;
	if (wpt->altitude != unknown_alt)
		le_write32(&ewpt.data.wp_altitude, METERS_TO_FEET(wpt->altitude) + 1000);
	if (wpt->shortname != NULL)
	{
        ewpt.shortname_len = min (6, strlen (wpt->shortname));
        strncpy(ewpt.shortname, wpt->shortname, 6);
	}
	if (wpt->description != NULL)
	{
        ewpt.longname_len = min (27, strlen (wpt->description));
        strncpy(ewpt.longname, wpt->description, 27);
	}
	gbfwrite(&ewpt, sizeof (ewpt), 1, file_out);
}

static void
data_write(void)
{
	route_disp_all(route_head_noop, route_head_noop, enigma_waypt_disp);
}

static void
wr_deinit(void)
{
	gbfclose(file_in);
}

ff_vecs_t enigma_vecs = {
		ff_type_file,
	{ 
		ff_cap_read | ff_cap_write,  	/* waypoints */
		ff_cap_none,                    /* tracks */
		ff_cap_read | ff_cap_write  	/* routes */
	},
		rd_init,
		wr_init,
		rd_deinit,
		wr_deinit,
		data_read,
		data_write,
		NULL,
		NULL,
	CET_CHARSET_ASCII, 0	/* CET-REVIEW */
};
